﻿using System;
using System.IO;
using System.Text;
using System.Threading;
using System.Windows.Forms;
using HtmlAgilityPack;
using HtmlDocument = HtmlAgilityPack.HtmlDocument;

namespace Downloader
{
  public partial class Form1 : Form
  {
    private FolderBrowserDialog _folderBrowserDialog;
    private ISimpleCache _cache;
    private const string URLPattern = "{0}&page={1}";
    protected string SAVE_DIR = "SAVE_DIR";

    public delegate void CompleteHandler();

    public Form1()
    {
      InitializeComponent();
      _folderBrowserDialog = new FolderBrowserDialog();
      _cache = new AppConfigCache();
    }

    private class Data
    {
      public String OriginalURL { get; set; }
      public int MaxPage { get; set; }
      public bool ConvertToUnicodeComposite { get; set; }
      public bool UsingTitleAsFileName;
      public bool UsingTopicIDAsFileName;
      public CompleteHandler Complete;
      public string SaveDir;
    }

    private String DownloadPage(String originalURL, int page)
    {
      String url = String.Format(URLPattern, originalURL, 1);
      return DownloadExtension.GetHtmlFromUrl(url);
    }

    private void Download(Data data)
    {
      String content = "";
      String fileName = "";

      // Download first page
      statusLabel.UpdateStatusText(string.Format("Downloading page {0}...", 1));

      String html = DownloadPage(data.OriginalURL, 1);
      if (data.UsingTitleAsFileName)
      {
        string title = DownloadExtension.GetTitleFromHTML(html);
        fileName = DownloadExtension.BuildFileNameFromTitle(title);
      }
      if (data.MaxPage <= 0)
      {
        // Find maxpage   
        data.MaxPage = DownloadExtension.GetMaxPageFromHTML(html);
      }

      // Update maximum for progressBar
      UpdateProgressBarMaximum(data);

      content += Parse(html);
      progressBar.IncreaseStep();

      for (int i = 2; i <= data.MaxPage; i++)
      {
        statusLabel.UpdateStatusText(string.Format("Downloading page {0}...", i));
        String url = String.Format(URLPattern, data.OriginalURL, i);

        string htmlContent = DownloadExtension.GetHtmlFromUrl(url);
        content += Parse(htmlContent);

        progressBar.IncreaseStep();
      }

      if (data.ConvertToUnicodeComposite)
      {
        statusLabel.UpdateStatusText("Converting ...");
        content = UnicodeExtension.ConvertToUnicodeComposite(content);
        progressBar.IncreaseStep();
      }

      if (data.UsingTopicIDAsFileName)
      {
        fileName = DownloadExtension.GetTopicIDFromURL(data.OriginalURL);
      }

      String fullPath = data.SaveDir + "\\" + fileName + ".html";

      statusLabel.UpdateStatusText(string.Format("Saving to {0} ...", fullPath));
      File.WriteAllText(fullPath, content, Encoding.UTF8);
      progressBar.IncreaseStep();

      statusLabel.UpdateStatusText("Completed!");
      data.Complete();
    }

    private delegate void UpdateProgressBarMaximumHandler(Data data);

    private void UpdateProgressBarMaximum(Data data)
    {
      if (progressBar.InvokeRequired)
      {
        var a = new UpdateProgressBarMaximumHandler(UpdateProgressBarMaximum);
        progressBar.Invoke(a, new object[] { data });
      }
      else
      {
        var maximum = data.MaxPage + 1;

        if (data.ConvertToUnicodeComposite)
        {
          maximum++;
        }

        progressBar.Maximum = maximum;
      }
    }

    private bool IsConvertToUnicodeComposite()
    {
      return unicodeComposite.Checked;
    }

    private static string Parse(string content)
    {
      HtmlDocument doc = new HtmlDocument();
      doc.LoadHtml(content);
      String ret = "";
      foreach (HtmlNode node in doc.DocumentNode.SelectNodes("//div[contains(@id, 'post_message_')]"))
      {
        ret += ForumExtension.RemoveObsolete(node.InnerHtml);
      }
      return ret;
    }

    /// <summary>
    /// When run button is clicked
    /// </summary>
    /// <param name="sender">The sender.</param>
    /// <param name="e">The <see cref="System.EventArgs"/> instance containing the event data.</param>
    private void RunButtonClick(object sender, EventArgs e)
    {
      int maxPage = int.Parse(maxPageTxt.Text);
      var maximum = maxPage + 1;

      var data = new Data()
      {
        OriginalURL = urlTxt.Text,
        MaxPage = maxPage,
        ConvertToUnicodeComposite = IsConvertToUnicodeComposite(),
        UsingTitleAsFileName = IsUsingTitleAsFileName(),
        UsingTopicIDAsFileName = IsUsingTopicIDAsFileName(),
        SaveDir = GetSaveDir(),
        Complete = () => runButton.SetEnabled(true),
      };
      progressBar.Value = 0;

      runButton.Enabled = false;

      Thread thread = new Thread(Start) { IsBackground = true };


      thread.Start(data);
    }

    private string GetSaveDir()
    {
      return saveDirTxt.Text;
    }

    private void SetSaveDir(String dir)
    {
      saveDirTxt.Text = dir;
    }

    private bool IsUsingTitleAsFileName()
    {
      return fileNameUsingTitleRadio.Checked;
    }

    private bool IsUsingTopicIDAsFileName()
    {
      return fileNameUsingTopicIDRadio.Checked;
    }

    /// <summary>
    /// Starts the specified obj.
    /// </summary>
    /// <param name="obj">The obj.</param>
    private void Start(Object obj)
    {
      var data = (Data)obj;

      if (data == null)
      {
        return;
      }

      try
      {
        Download(data);
      }
      catch (Exception e)
      {
        MessageBox.Show(string.Format("Error when download: {0}", e), "Topic Crawler");
        data.Complete();
      }
    }

    /// <summary>
    /// Handles the Click event of the saveDirButton control.
    /// </summary>
    /// <param name="sender">The source of the event.</param>
    /// <param name="e">The <see cref="System.EventArgs"/> instance containing the event data.</param>
    private void saveDirButton_Click(object sender, EventArgs e)
    {
      DialogResult result = _folderBrowserDialog.ShowDialog(this);
      if (result != DialogResult.OK)
      {
        return;
      }

      SetSaveDir(_folderBrowserDialog.SelectedPath);
      _cache.Save(SAVE_DIR, GetSaveDir());
    }

    private void Form1_FormClosed(object sender, FormClosedEventArgs e)
    {
      _cache.Save();
    }

    private void Form1_Load(object sender, EventArgs e)
    {
      String savedDir = _cache.Get(SAVE_DIR);
      if (!Directory.Exists(savedDir))
      {
        savedDir = "";
      }

      SetSaveDir(savedDir);
      _folderBrowserDialog.SelectedPath = savedDir;
    }
  }
}
